package flex.cc.store.util;

import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

/**
 * @Author: wby
 * @Description: 上游签名工具类
 * @Date: Created in 16:18  2019/5/14
 * @param:
 * @return:
 */
public class SdSignUtil {
	private static final Logger logger = LoggerFactory.getLogger(SdSignUtil.class);
    /**
     * MD5:生成 MD5
     * 
     * @param data 待处理数据
     * @return MD5结果
     * @throws Exception
     * @since JDK 1.7
     */
    public static String MD5(String data) throws Exception {

        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * HMACSHA256:生成 HMACSHA256
     * 
     * @param data 待处理数据
     * @param key 密钥
     * @return 加密结果
     * @throws Exception
     * @since JDK 1.7
     */
    public static String HMACSHA256(String data, String key) throws Exception {

        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    public static void main(String[] args) throws Exception {
		String jsonStr = "GH@Drg#Test{\"apiSource\":\"GHMY\",\"id\":\"202006268181\",\"UserCard\":{\"expireTime\":\"2023-03-31 16:23:47\",\"cardType\":\"5044\",\"customerCode\":412414134,\"startTime\":\"2022-03-31 16:23:47\"},\"source\":\"GHMY\"}";
		String s = DigestUtils.md5Hex(jsonStr.getBytes("UTF-8"));
        System.out.println(s);
    }
    
    /**
     * 生成签名. 注意，若含有sign_type字段，必须和signType参数保持一致。
     * 
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     * @throws Exception
     * @since JDK 1.7
     */
    public static String generateSignature(final Map<String, Object> data, String key, String signType) throws Exception {

        Map<String, String> params = filterBlank(data);
        Set<String> keySet = params.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals("sign")) {
                continue;
            }
            if (params.get(k).trim().length() > 0) { //参数值为空，则不参与签名
                sb.append(k).append("=").append(params.get(k).trim()).append("&");
            }
        }
        sb.append("key=").append(key);
        
        logger.info("sb.toString()---------"+sb.toString());
        if ("MD5".equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        } else if ("HMACSHA256".equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        } else if("AES".equals(signType)){
            return AESSingleUtil.encrypt(sb.toString(), key);
        }else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }


    public static String generateSignatureObject(final Map<String, Object> data, String key, String signType) throws Exception {

        Map<String, Object> params = filterBlankObj(data);
        Set<String> keySet = params.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals("sign")) {
                continue;
            }
            if (params.get(k).toString().length() > 0) { //参数值为空，则不参与签名
                sb.append(k).append("=").append(params.get(k).toString().trim()).append("&");
            }
        }
        sb.append("key=").append(key);
        if ("MD5".equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        } else if ("HMACSHA256".equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        } else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }


    public static String generateSignatureObjectByBopai(final Map<String, Object> data, String key, String signType) throws Exception {

        Map<String, Object> params = filterBlankObj(data);
        Set<String> keySet = params.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray, Collections.reverseOrder());
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals("sign")) {
                continue;
            }
            if (params.get(k).toString().length() > 0) { //参数值为空，则不参与签名
                sb.append(k).append("=").append(params.get(k).toString().trim()).append("&");
            }
        }
        sb.append(key);
        if ("MD5".equals(signType)) {
            return MD5(sb.toString());
        } else if ("HMACSHA256".equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        } else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }

    /**
     * 判断签名是否正确，必须包含sign字段，否则返回false。
     * 
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名是否正确
     * @since JDK 1.7
     */
    public static boolean isSignatureValid(Map<String, Object> data, String key, String signType) {
    	
    	
    	
        if (!data.containsKey("sign")) {
            return false;
        }
        String sign = data.get("sign").toString();
        logger.info("request sign: "+ sign);
        try {
        	logger.info("系统开始生成 sign: ");
        	String newSign = generateSignature(data, key, signType);
        	logger.info("系统生成 sign: "+ newSign);
        	System.out.println("系统生成 sign: "+ newSign);
            return newSign.equals(sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * filterBlank:过滤请求报文中的空字符串或者空字符串
     * 
     * @param contentData 待过滤的数据
     * @return
     * @since JDK 1.7
     */
    public static Map<String, String> filterBlank(Map<String, Object> contentData) {

        Map<String, String> submitFromData = new HashMap<String, String>();
        Set<String> keyset = contentData.keySet();
        for (String key : keyset) {
            String value = contentData.get(key).toString();
            if (!StringUtils.isBlank(value)) {
                submitFromData.put(key, value.trim());
            }
        }
        return submitFromData;
    }


    public static Map<String, Object> filterBlankObj(Map<String, Object> contentData) {

        Map<String, Object> submitFromData = new HashMap<String, Object>();
        Set<String> keyset = contentData.keySet();
        for (String key : keyset) {
            Object value = contentData.get(key);
            if (null!=value&&!StringUtils.isBlank(value.toString())) {
                submitFromData.put(key, value.toString().trim());
            }
        }
        return submitFromData;
    }

    /**
     * shortMD5:16位MD5加密算法
     * 
     * @param plainText
     * @return
     * @throws Exception
     * @since JDK 1.7
     */
    public static String shortMD5(String plainText) throws Exception {

        return MD5(plainText).substring(8, 24);
    }
}
